<cfcomponent output="false" entityname="XTemplate">
	<cfset this.html = "">
	<cfset this.values = {}>
	<cfset this.tplRe = "<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>">
	<cfset this.tpRe = '^<tpl\b[^>]*?'>
	<cfset this.forRe = '^<tpl\b[^>]*?for="(.*?)"'>
	<cfset this.ifRe = '^<tpl\b[^>]*?if="(.*?)"'>
	
	<cffunction name="init" returntype="any" output="false">
		<cfset var s = "">
		<cfloop collection="#arguments#" item="x">
			<cfset s &= arguments[x]>
		</cfloop>
 		<cfset s = ['<tpl>',s,'</tpl>']>
		<cfset this.html = arrayToList(s,'')>
		<cfreturn this>
	</cffunction>
	
	<cffunction name="set" returntype="any" output="false">
		<cfargument name="values" type="any" required="true">
		<cfset this.values = arguments.values>
		<cfreturn this>
	</cffunction>
	
	<cffunction name="render" returntype="string" output="false">
		<cfargument name="html" type="string">
		<cfargument name="values" type="any">
		<cfargument name="xid" type="numeric" default="0">
		<cfset var rt = {}>
		<cfif NOT structKeyExists(arguments,'html')>
			<cfset arguments.html = this.html>
		</cfif>
		<cfif NOT structKeyExists(arguments,'values')>
			<cfset arguments.values = this.values>
		</cfif>
		<cfset rt.aTpl = this.match(this.tpRe,arguments.html)>
		<cfset rt.aFor = this.match(this.forRe,arguments.html)>
		<cfset rt.aIf = this.match(this.ifRe,arguments.html)>
		<cfif arrayLen(rt.aIf) GT 0>
		<cfelseif arrayLen(rt.aFor) GT 0 AND structKeyExists(arguments.values,rt.aFor[2]) AND isArray(arguments.values[rt.aFor[2]])>
			<cfset arguments.values = arguments.values[rt.aFor[2]]>
			<cfset rt.subHTML = replace(arguments.html,rt.aFor[1]&">","")>
			<cfset rt.subHTML = reverse(replace(reverse(rt.subHTML),">lpt/<",""))>
			<cfset rt.innerTpls = this.tplMatch(rt.subHTML)>
			<cfset rt.forHTML = []>
			<cfloop from="1" to="#arrayLen(rt.innerTpls)#" index="i">
				<cfset rt.subHTML = replace(rt.subHTML,rt.innerTpls[i],"{xid:"&i&"}","all")>
			</cfloop>
			<cfloop array="#arguments.values#" index="locVarName">
				<cfif isSimpleValue(locVarName)>
					<cfset tmpHTML = replace(rt.subHTML,"{.}",locVarName)>
				<cfelse>
					<cfset tmpHTML = this.renderStruct(rt.subHTML,locVarName)>
					<cfloop from="1" to="#arrayLen(rt.innerTpls)#" index="i">
						<cfset tmpHTML = replace(tmpHTML,"{xid:"&i&"}",this.render(rt.innerTpls[i],locVarName),"all")>
					</cfloop>
				</cfif>
				<cfset arrayAppend(rt.forHTML,tmpHTML)>
			</cfloop>
			<cfset rt.subHTML = arrayToList(rt.forHTML,"")>
		<cfelseif arrayLen(rt.aTpl) GT 0>
			<cfset rt.subHTML = replace(arguments.html,rt.aTpl[1]&">","")>
			<cfset rt.subHTML = reverse(replace(reverse(rt.subHTML),">lpt/<",""))>
			<cfset rt.innerTpls = this.tplMatch(rt.subHTML)>
			<cfloop from="1" to="#arrayLen(rt.innerTpls)#" index="i">
				<cfset rt.subHTML = replace(rt.subHTML,rt.innerTpls[i],"{xid:"&i&"}","all")>
			</cfloop>
			<cfset rt.subHTML = this.renderStruct(rt.subHTML,arguments.values)>
			<cfloop from="1" to="#arrayLen(rt.innerTpls)#" index="i">
				<cfset rt.subHTML = replace(rt.subHTML,"{xid:"&i&"}",this.render(rt.innerTpls[i],arguments.values),"all")>
			</cfloop>
		</cfif>
		<cfreturn rt.subHTML>
	</cffunction>
	
	<cffunction name="renderStruct" returntype="string" output="false" access="package">
		<cfargument name="html" type="string" required="true">
		<cfargument name="values" type="struct" required="true">
		<cfloop collection="#arguments.values#" item="varName">
			<cfif isSimpleValue(arguments.values[varName])>
				<cfset arguments.html = replaceNoCase(arguments.html,"{"&varName&"}",arguments.values[varName])>
			</cfif>
		</cfloop>
		<cfreturn arguments.html>
	</cffunction>
	
	<cffunction name="tplMatch" returntype="array" output="true" access="package">
		<cfargument name="html" type="string">
		<cfset var aTpl = reFindRecursive("<[\/]*tpl\b[^>]*>",arguments.html)>
		<cfset var pos = 0>
		<cfset var rt = {}>
		<cfset var rtn = []>
		<cfloop from="1" to="#arraylen(aTpl.pos)#" index="i">
			<cfset sec = aTpl.match[i]>
			<cfif find("<tpl",sec)>
				<cfif pos EQ 0>
					<cfset rt.start = aTpl.pos[i][1]>
				</cfif>
				<cfset pos++>
			</cfif>
			<cfif find("</tpl>",sec)>
				<cfset pos-->
			</cfif>
			<cfif pos EQ 0>
				<cfset rt.end = (aTpl.pos[i][1]+aTpl.len[i][1])-rt.start>
				<cfset rt.html = mid(arguments.html,rt.start,rt.end)>
				<cfset arrayAppend(rtn,rt.html)>
			</cfif>
		</cfloop>
		<cfreturn rtn>
	</cffunction>
	
	<cffunction name="reFindRecursive" output="false" returntype="struct" access="package">
		<cfargument name="ReGex" type="string" required="true">
		<cfargument name="String" type="string" required="true">
		<cfset var rtn = {len=[],pos=[],match=[]}>
		<cfset var loop = true>
		<cfset var pos = 0>
		<cfloop condition="loop EQ true">
			<cfset reF = reFind(arguments.ReGex,arguments.String,pos,true)>
			<cfif reF.pos[1] GT 0>
				<cfset arrayAppend(rtn.len,reF.len)>
				<cfset arrayAppend(rtn.pos,reF.pos)>
				<cfset arrayAppend(rtn.match,mid(arguments.string,reF.pos[1],ref.len[1]))>
				<cfset pos = reF.pos[1]+reF.len[1]>
				<cfset loop = true>
			<cfelse>
				<cfset loop = false>
			</cfif>
		</cfloop>
		<cfreturn rtn>
	</cffunction>
	
	<cffunction name="match" returntype="Array" output="false" description="Sames as match for javascript" access="package">
		<cfargument name="reg_expression" type="string" required="true" hint="Regular expression for which to search. Case sensitive.">
		<cfargument name="string" type="string" required="true">
		<cfset var reM = reFind(arguments.reg_expression,arguments.string,0,true)>
		<cfset var matches = []>
		<cfloop from="1" to="#arrayLen(reM.pos)#" index="i"><cfif reM.pos[i] GT 0 AND reM.len[i] GT 0>
			<cfset arrayAppend(matches,mid(arguments.string,reM.pos[i],reM.len[i]))></cfif>
		</cfloop>
		<cfreturn matches>
	</cffunction>
</cfcomponent>